package np13.ferry;

import java.util.LinkedList;

import np13.Configuration;
import np13.Slime;
import np13.Smell;

/**
 * A Ferry sails from the Funafitu atoll to a fixed homeland back and forth
 * carrying some slimes.
 */
public class Ferry {

	/**
	 * The pier of the target land. Note that this value will change depending
	 * on where the Ferry is currently located (funafitu or homeland).
	 */
	private Pier remotePier;

	/**
	 * The pier, which the Ferry is currently connected to. Note that this value
	 * will change depending on where the Ferry is currently located (funafitu
	 * or homeland).
	 */
	private Pier currentPier;

	/**
	 * True, if the Ferry is sailing, false otherwise.
	 */
	boolean isSailing = false;

	/**
	 * The smell of the homeland, which the Ferry is sailing to.
	 */
	private final Smell smell;

	/**
	 * The slimes which are carried by the Ferry.
	 */
	private final LinkedList<Slime> content;

	/**
	 * The capacity of the Ferry.
	 */
	private final int size;

	/**
	 * Initializes a Ferry.
	 * 
	 * @param smell
	 *            The smell of the homeland, which the Ferry is sailing to.
	 * @param startPier
	 *            The pier where the Ferry initially starts on Funafitu.
	 * @param otherPier
	 *            The pier connected to the homeland.
	 * @param size
	 *            The capacity of the Ferry.
	 */
	public Ferry(Smell smell, Pier startPier, Pier otherPier, int size) {
		if (startPier == null || otherPier == null)
			throw new IllegalArgumentException();

		this.smell = smell;
		this.currentPier = startPier;
		this.remotePier = otherPier;
		currentPier.setLocation(smell.name());
		remotePier.setLocation("Funafitu");

		this.size = size;
		this.content = new LinkedList<>();
	}

	/**
	 * Lets the Ferry arrive on a land.
	 */
	private void arrive() {
		isSailing = false;
		currentPier.arrive(this);
	}

	/**
	 * Lets the Ferry depart from the current Pier.
	 */
	private void depart() {
		currentPier.leave();
		isSailing = true;
		final Pier t = currentPier;
		currentPier = remotePier;
		remotePier = t;
	}

	/**
	 * 
	 * @return Returns the initialized capacity of a Ferry.
	 */
	public int getCapacity() {
		return size;
	}

	/**
	 * @return The pier, which the Ferry is currently connected to. Note that
	 *         this value will change depending on where the Ferry is currently
	 *         located (funafitu or homeland).
	 */
	public Pier getCurrentPier() {
		return currentPier;
	}

	/**
	 * @return The pier of the target land. Note that this value will change
	 *         depending on where the Ferry is currently located (funafitu or
	 *         homeland).
	 */
	public Pier getRemotePier() {
		return remotePier;
	}

	/**
	 * Gets the current amount of slimes.
	 * 
	 * @return the amount of slimes
	 */
	public int getSize() {
		return content.size();
	}

	/**
	 * @return The smell of the homeland the Ferry is sailing to.
	 */
	public Smell getSmell() {
		return smell;
	}

	/**
	 * @return True, if the Ferry is currently sailing, false otherwise.
	 */
	public boolean isSailing() {
		return isSailing;
	}

	/**
	 * Removes the first slime from the Ferry.
	 * 
	 * @return The first enqueued slime.
	 */
	public Slime popSlime() {
		assert !content.isEmpty();
		return content.pop();
	}

	/**
	 * Puts a slime into the Ferry.
	 * 
	 * @param slime
	 *            The slime to be added.
	 */
	public void pushSlime(Slime slime) {
		assert (content.size() < size);
		content.add(slime);
	}

	/**
	 * Starts the Ferry. The Ferry will automatically sail towards the remote
	 * pier. The Ferry will sail asynchronously: It will immediately depart from
	 * the current pier, and then, this method will return. After some time (see
	 * {@link Configuration}, the Ferry will try to arrive at the new pier.
	 */
	public void sail() {
		if (isSailing)
			throw new IllegalStateException();
		depart();

		final Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(Configuration.getFerryTravelDuration(smell));
					arrive();
				} catch (final InterruptedException e) {
					Thread.currentThread().interrupt();
				}
			}
		});
		thread.start();
	}
}
